using System.Text.Json;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp;
using Microsoft.CodeAnalysis.CSharp.Syntax;

namespace CSharpAnalyzer.AnalyzerService.Serialization;

/// <summary>
/// Serializes Roslyn syntax trees to JSON format
/// </summary>
public class SyntaxSerializer
{
    private readonly JsonSerializerOptions _jsonOptions;

    /// <summary>
    /// Initializes a new instance of the SyntaxSerializer
    /// </summary>
    public SyntaxSerializer()
    {
        _jsonOptions = new JsonSerializerOptions
        {
            WriteIndented = true,
            PropertyNamingPolicy = JsonNamingPolicy.CamelCase
        };
    }

    /// <summary>
    /// Serializes a document's syntax tree to JSON
    /// </summary>
    /// <param name="document">The document to serialize</param>
    /// <param name="cancellationToken">Cancellation token</param>
    /// <returns>JSON representation of the syntax tree</returns>
    public async Task<string> SerializeDocumentAsync(Document document, CancellationToken cancellationToken = default)
    {
        if (document == null)
        {
            throw new ArgumentNullException(nameof(document));
        }

        var syntaxTree = await document.GetSyntaxTreeAsync(cancellationToken);
        if (syntaxTree == null)
        {
            return JsonSerializer.Serialize(new { error = "Unable to get syntax tree" }, _jsonOptions);
        }

        var root = await syntaxTree.GetRootAsync(cancellationToken);
        var compilationUnit = root as CompilationUnitSyntax;

        if (compilationUnit == null)
        {
            return JsonSerializer.Serialize(new { error = "Root is not a CompilationUnitSyntax" }, _jsonOptions);
        }

        var documentInfo = new DocumentSyntaxInfo
        {
            FileName = document.Name,
            FilePath = document.FilePath ?? string.Empty,
            Classes = ExtractClassDeclarations(compilationUnit)
        };

        return JsonSerializer.Serialize(documentInfo, _jsonOptions);
    }

    /// <summary>
    /// Extracts class declarations from a compilation unit
    /// </summary>
    private List<ClassInfo> ExtractClassDeclarations(CompilationUnitSyntax compilationUnit)
    {
        var classes = new List<ClassInfo>();

        // Find all class declarations in the compilation unit
        var classDeclarations = compilationUnit.DescendantNodes()
            .OfType<ClassDeclarationSyntax>();

        foreach (var classDecl in classDeclarations)
        {
            var classInfo = new ClassInfo
            {
                Name = classDecl.Identifier.Text,
                Namespace = GetNamespace(classDecl),
                Modifiers = classDecl.Modifiers.Select(m => m.Text).ToList(),
                BaseTypes = classDecl.BaseList?.Types.Select(t => t.ToString()).ToList() ?? new List<string>(),
                Members = ExtractMembers(classDecl)
            };

            classes.Add(classInfo);
        }

        return classes;
    }

    /// <summary>
    /// Gets the namespace for a class declaration
    /// </summary>
    private string GetNamespace(ClassDeclarationSyntax classDecl)
    {
        // Walk up the syntax tree to find namespace
        var namespaceDecl = classDecl.Ancestors()
            .OfType<BaseNamespaceDeclarationSyntax>()
            .FirstOrDefault();

        return namespaceDecl?.Name.ToString() ?? string.Empty;
    }

    /// <summary>
    /// Extracts member information from a class
    /// </summary>
    private List<MemberInfo> ExtractMembers(ClassDeclarationSyntax classDecl)
    {
        var members = new List<MemberInfo>();

        foreach (var member in classDecl.Members)
        {
            var memberInfo = member switch
            {
                MethodDeclarationSyntax method => new MemberInfo
                {
                    Kind = "Method",
                    Name = method.Identifier.Text,
                    Modifiers = method.Modifiers.Select(m => m.Text).ToList(),
                    ReturnType = method.ReturnType.ToString(),
                    Parameters = method.ParameterList.Parameters.Select(p => $"{p.Type} {p.Identifier}").ToList()
                },
                PropertyDeclarationSyntax property => new MemberInfo
                {
                    Kind = "Property",
                    Name = property.Identifier.Text,
                    Modifiers = property.Modifiers.Select(m => m.Text).ToList(),
                    ReturnType = property.Type.ToString(),
                    Parameters = new List<string>()
                },
                FieldDeclarationSyntax field => new MemberInfo
                {
                    Kind = "Field",
                    Name = string.Join(", ", field.Declaration.Variables.Select(v => v.Identifier.Text)),
                    Modifiers = field.Modifiers.Select(m => m.Text).ToList(),
                    ReturnType = field.Declaration.Type.ToString(),
                    Parameters = new List<string>()
                },
                ConstructorDeclarationSyntax constructor => new MemberInfo
                {
                    Kind = "Constructor",
                    Name = constructor.Identifier.Text,
                    Modifiers = constructor.Modifiers.Select(m => m.Text).ToList(),
                    ReturnType = string.Empty,
                    Parameters = constructor.ParameterList.Parameters.Select(p => $"{p.Type} {p.Identifier}").ToList()
                },
                _ => null
            };

            if (memberInfo != null)
            {
                members.Add(memberInfo);
            }
        }

        return members;
    }
}

/// <summary>
/// Represents document syntax information
/// </summary>
public class DocumentSyntaxInfo
{
    public string FileName { get; set; } = string.Empty;
    public string FilePath { get; set; } = string.Empty;
    public List<ClassInfo> Classes { get; set; } = new();
}

/// <summary>
/// Represents a class declaration
/// </summary>
public class ClassInfo
{
    public string Name { get; set; } = string.Empty;
    public string Namespace { get; set; } = string.Empty;
    public List<string> Modifiers { get; set; } = new();
    public List<string> BaseTypes { get; set; } = new();
    public List<MemberInfo> Members { get; set; } = new();
}

/// <summary>
/// Represents a class member (method, property, field, etc.)
/// </summary>
public class MemberInfo
{
    public string Kind { get; set; } = string.Empty;
    public string Name { get; set; } = string.Empty;
    public List<string> Modifiers { get; set; } = new();
    public string ReturnType { get; set; } = string.Empty;
    public List<string> Parameters { get; set; } = new();
}
